package dao

import (
	"context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/status"
	"prod/api/bilinadmin"
	"prod/api/prod"
	"prod/internal/conf"
	"prod/internal/constant"
	"prod/pkg/ecode"
	"prod/pkg/log"
	"prod/pkg/network"
	"prod/pkg/request_key"
	"time"
)

func newAdminConn(ctx context.Context, adminAddr string) (conn *grpc.ClientConn, err error) {
	conn, err = grpc.Dial(adminAddr, grpc.WithInsecure(),
		grpc.WithUnaryInterceptor(network.ClientInterceptor(
			"bilinadmin", time.Second*3, adminErrorTranslator)))
	if err != nil {
		err = ecode.AppendMessage(constant.ErrRemoteError, "比邻品牌服务地址无法建立有效连接, err=%s", err.Error())
		log.FromContext(ctx).Warnw(err.Error())
		return
	}
	return
}

func newAdminClient(ctx context.Context, adminAddr string) (
	client bilinadmin.BilinAdminClient, err error) {
	var conn *grpc.ClientConn
	if conn, err = newAdminConn(ctx, adminAddr); err != nil {
		return
	}
	client = bilinadmin.NewBilinAdminClient(conn)
	return
}

func newEmployeeClient(ctx context.Context, adminAddr string) (
	client bilinadmin.BilinEmployeeClient, err error) {
	var conn *grpc.ClientConn
	if conn, err = newAdminConn(ctx, adminAddr); err != nil {
		return
	}
	client = bilinadmin.NewBilinEmployeeClient(conn)
	return
}

func newPermissionClient(ctx context.Context, adminAddr string) (
	client bilinadmin.BilinPermissionClient, err error) {
	var conn *grpc.ClientConn
	if conn, err = newAdminConn(ctx, adminAddr); err != nil {
		return
	}
	client = bilinadmin.NewBilinPermissionClient(conn)
	return
}

func adminErrorTranslator(err error) (errCode *ecode.ErrorCode) {
	errCode = ecode.AppendMessage(
		constant.ErrRemoteError, "比邻品牌服务调用失败：%s", status.Convert(err).Message())
	return
}

func (d *Dao) checkPermission(ctx context.Context, webToken *prod.WebToken, pStoreId uint32, route string) (err error) {
	var reply *bilinadmin.PermissionCheckReply
	if d.permissionClient == nil {
		if d.permissionClient, err = newPermissionClient(
			ctx, conf.Conf.Downstream.AdminAddress); err != nil {
			return
		}
	}
	clientCtx, cancel := request_key.NewOutGoingContext(ctx, time.Second*3)
	defer cancel()
	if reply, err = d.permissionClient.PermissionCheck(clientCtx, &bilinadmin.PermissionCheckReq{
		BrandId: int64(webToken.SgId),
		StoreId: int64(pStoreId),
		EmpId:   int64(webToken.EmpId),
		Router:  route,
	}); err != nil {
		return err
	}
	if reply.HasPermission {
		return
	}
	err = constant.ErrPermissionError
	return
}

func (d *Dao) CheckAdmin(ctx context.Context, webToken *prod.WebToken, pStoreId uint32) (err error) {
	isHTTP, _, _ := network.GetHttpCallInfo(ctx)
	if !isHTTP {
		// rpc请求已在其他系统校验过权限，可以跳过
		return
	}
	err = d.checkPermission(ctx, webToken, pStoreId, "admin")
	return
}

func (d *Dao) CheckPermission(ctx context.Context, webToken *prod.WebToken, pStoreId uint32) (err error) {
	isHTTP, _, _ := network.GetHttpCallInfo(ctx)
	if !isHTTP {
		// rpc请求已在其他系统校验过权限，可以跳过
		return
	}
	err = d.checkPermission(ctx, webToken, pStoreId, "product")
	return
}

func (d *Dao) GetStoreMapByStoreIds(ctx context.Context, storeIds []uint32) (
	storeMap map[uint32]*prod.StoreSimpleView, err error) {
	var stores []*prod.StoreSimpleView
	if stores, err = d.GetStoreListByStoreIds(ctx, storeIds); err != nil {
		return
	}
	storeMap = make(map[uint32]*prod.StoreSimpleView)
	for _, store := range stores {
		storeMap[store.Id] = store
	}
	return
}

func (d *Dao) GetStoreListByStoreIds(ctx context.Context, storeIds []uint32) (
	stores []*prod.StoreSimpleView, err error) {
	if len(storeIds) == 0 {
		return
	}
	var (
		reply         *bilinadmin.StoreNamesReply
		inputStoreIds []int64
	)
	for _, storeId := range storeIds {
		inputStoreIds = append(inputStoreIds, int64(storeId))
	}
	if d.adminClient == nil {
		if d.adminClient, err = newAdminClient(
			ctx, conf.Conf.Downstream.AdminAddress); err != nil {
			return
		}
	}
	clientCtx, cancel := request_key.NewOutGoingContext(ctx, time.Second*3)
	defer cancel()
	if reply, err = d.adminClient.StoreNamesGetByIds(clientCtx, &bilinadmin.StoreNamesGetByIdsReq{
		StoreIds: inputStoreIds,
	}); err != nil {
		return
	}
	for _, store := range reply.Stores {
		stores = append(stores, &prod.StoreSimpleView{
			Id:   uint32(store.StoreId),
			Name: store.StoreName,
		})
	}
	return
}
